//"use strict";
// need
// 8位计算能力 + - 移位
// 3k rom 512 ram
// keyboard/joystick input
// display
// random
// timer

#include "lib/key.h"

#define TETRIS_RES 0x00000000


int _block[] = {//
	0x070, 0x034, 0x038, 0x06C,//
	0x050, 0x021, 0x022, 0x043,//
	0x070, 0x034, 0x038, 0x06C,//
	0x050, 0x021, 0x022, 0x043,// ;1 长条
	0x0B0, 0x091, 0x084, 0x0A5,//
	0x0B0, 0x091, 0x084, 0x0A5,//
	0x0B0, 0x091, 0x084, 0x0A5,//
	0x0B0, 0x091, 0x084, 0x0A5,// ;2 方
	0x071, 0x0B4, 0x0A5, 0x068,//
	0x050, 0x091, 0x085, 0x046,//
	0x071, 0x0B4, 0x0A5, 0x068,//
	0x050, 0x091, 0x085, 0x046,// ;3 Z
	0x070, 0x084, 0x095, 0x069,//
	0x0B1, 0x042, 0x054, 0x0A5,//
	0x070, 0x084, 0x095, 0x069,//
	0x0B1, 0x042, 0x054, 0x0A5,// ;4 反Z
	0x070, 0x034, 0x088, 0x049,//
	0x072, 0x054, 0x025, 0x0A6,//
	0x050, 0x091, 0x035, 0x069,//
	0x0B0, 0x021, 0x042, 0x064,// ;5 反L
	0x0B0, 0x041, 0x034, 0x068,//
	0x070, 0x084, 0x025, 0x046,//
	0x071, 0x035, 0x058, 0x0A9,//
	0x050, 0x021, 0x092, 0x066,// ;6 L
	0x070, 0x0E4, 0x045, 0x068,//
	0x071, 0x054, 0x0F5, 0x046,//
	0x071, 0x054, 0x0C5, 0x069,//
	0x050, 0x0D1, 0x042, 0x065 // ;7 T
	};
int _chg[] = {0x000, 0x011, 0x045, 0x033, 0x041, 0x015, 0x066, 0x077, 0x068, 0x097, 0x0A6, 0x07B, 0x0C3, 0x09B, 0x03E, 0x0A8};

class Tetris {
  public:
	int Board[200];
	int PosX;
	int PosY;
	int NextShapeNo;
	int NowShapeNo;
	int NowDirectionNo;

  int (*CallBack_GetRandom)(void);
	//int CallBack_GetRandom(){
  //  return *rnd7;
	//};
  
  //int (*CallBack_DrawNextShape)(Tetris*);
	//int CallBack_DrawNextShape() {
  //  call GUI
	//},

	int Public_Init() {
		private_Clear();
		private_NextShape();
		private_NextShape();
	};
  
	int Public_Down() {
		PosY--;
		if (private_isTouch()) {
			PosY++;
			private_touchDo();
			return false;
		} else {
			return true;
		}
	};
  
	int Public_Move(int n) {
		PosX += n;
		if (private_isTouch()) {
			PosX -= n;
		}
	};
  
	int Public_Rotate() {
		NowDirectionNo = (NowDirectionNo + 1) & 3;
		if (private_isTouch()) {
			PosX--;
			if (private_isTouch()) {
				PosX--;
				if (private_isTouch()) {
					PosX--;
					if (private_isTouch()) {
						PosX += 3;
						NowDirectionNo = (NowDirectionNo + 3) & 3;
					}
				}
			}
		}
	};
  
	int private_touchDo() {
		// add to board
		for (int idx = 0; idx < 4; idx++) {
			int block = Public_GetBlock(NowShapeNo, NowDirectionNo, idx);
			int x = PosX + Public_GetBlockXout;
			int y = PosY - Public_GetBlockYout;
			private_setBoard(x, y, block);
		}

		// clear line
		for (int j = 19; j >= 0; j--) {
			int line = 1;
			for (int i = 0; i < 10; i++) {
				if (private_getBoard(i, j) == 0) {
					line = 0;
					break;
				}
			}
			if (line == 1) {
				for (int k = 0; k < 10; k++) {
					if (j - 1 >= 0) {
						int temp = private_getBoard(k, j - 1);
						temp = _chg[temp] & 0x0F;
						private_setBoard(k, j - 1, temp);
					}
					if (j + 1 < 20) {
						int temp = private_getBoard(k, j + 1);
						temp = (_chg[temp] >> 4) & 0x0F;
						private_setBoard(k, j + 1, temp);
					}
					private_setBoard(k, j, 0);
				}
				for (int k = j; k < 19; k++) {
					for (int l = 0; l < 10; l++) {
						private_setBoard(l, k, private_getBoard(l, k + 1));
					}
				}
			}
		}

		// is gameover
		for (int i = 0; i < 10; i++) {
			if (private_getBoard(i, 18) != 0) {
				private_Clear();
				break;
			}
		}
		private_NextShape();
	};
  
	int private_getBoard(int x, int y) {
		return Board[x + y * 10];
	};
  
	int private_setBoard(int x, int y, int val) {
		Board[x + y * 10] = val;
	};

	int Public_GetBlockXout;
	int Public_GetBlockYout;
	int Public_GetBlock(int ShapeNo, int DirectionNo, int idx) {
		int dat = _block[(ShapeNo << 4) + (DirectionNo << 2) + idx];
		Public_GetBlockYout = dat & 3;// (0000 00XX)
		dat >>= 2;
		Public_GetBlockXout = dat & 3;// (0000 XX00)
		dat >>= 2;
		return dat;// (xxxx 0000)
	};
  
	int private_isTouch() {
		for (int idx = 0; idx < 4; idx++) {
			int block = Public_GetBlock(NowShapeNo, NowDirectionNo, idx);
			int x = PosX + Public_GetBlockXout;
			int y = PosY - Public_GetBlockYout;
			if (y < 0 || y > 19 || x < 0 || x > 9) {
				return true;
			}
			if (private_getBoard(x, y) != 0) {
				return true;
			}
		}
		return false;
	};
  
	int private_NextShape() {
		NowShapeNo = NextShapeNo;
		NextShapeNo = CallBack_GetRandom();
		NowDirectionNo = 0;
		PosY = 19;
		PosX = 3;
	};
  
	int private_Clear() {
		for (int i = 0; i < 10; i++) {
			for (int j = 0; j < 20; j++) {
				private_setBoard(i, j, 0);
			}
		}
	}
};

int rnd(int max){
  int v = ::rnd() & 0xFFFF;
  v = v * max;
  v = v >> 16;
  return v;
}

int CallBack_GetRandom() {
    //int val = rnd(7);
    //printInt(val);print("\r\n");
  return rnd(7);//0~6
};


void onTetrisTimer(Timer_win* timer);

class FormTetris : public Form
{
  public:
  int rnd7;
  int res;
  Tetris tetris;
  int lastBoard[200];
  int currBoard[200];
  int lastNext[16];
  int currNext[16];
  Timer_win* timer;
  int file_fileSize_;

  void InitBoard() {
    for(int i=0;i<200;i++){
      lastBoard[i] = -1;
    }
    for(int i=0;i<16;i++){
      lastNext[i] = -1;
    }
  }

  void init(){
    Form::init("Tetris");
    id = 123;
    width = 450;
    height = 550;
    pos_x = 50;
    pos_y = 20;
    //b->text="timer";
    color = getRGB565(0x0000000);
    
    InitBoard();
    
    tetris.CallBack_GetRandom = CallBack_GetRandom;
    tetris.Public_Init();
    
    res = (int)__getRes__();
    int posx = 0;
    int index = 0;

    {
      timer = new Timer_win();
      timer->interval = 900;//ms
      timer->nextTick = 0;//ms
      timer->pOnTime = onTetrisTimer;
      ::windows->addTimer(timer);
    }

    
    regClose(FormTetris::close);
    regKeyEvent(FormTetris::keyEvent);
    regDispose(FormTetris::dispose);
    regDraw(FormTetris::draw);
  }

  static void keyEvent(Window* self, int key, int chr){
    printf("key %d, %d", key, chr);
    FormTetris* form = (FormTetris*)self;
    if (key == KEY_F || key == KEY_SPACE){
      form->tetris.Public_Rotate();
    }else if (key == KEY_D || key == KEY_ARROW_RIGHT){
      form->tetris.Public_Move(1);
    }else if (key == KEY_A || key == KEY_ARROW_LEFT){
      form->tetris.Public_Move(-1);
    }else if (key == KEY_S || key == KEY_ARROW_DOWN){
      form->tetris.Public_Down();
    }else if (key == KEY_X) {
      while (form->tetris.Public_Down());
    }
    form->DrawBoard();
  }
  

  void onTimer() {
    tetris.Public_Down();
    DrawBoard();
  }

  void DrawBoard() {

    for (int j = 0; j < 20; j++) {
      for (int i = 0; i < 10; i++) {
        int block = tetris.Board[i + (19 - j) * 10];
        currBoard[i+j*10] = block;
      }
    }
    
    for (int idx = 0; idx < 4; idx++) {
      int block = tetris.Public_GetBlock(tetris.NowShapeNo, tetris.NowDirectionNo, idx);
      int x = tetris.PosX + tetris.Public_GetBlockXout;
      int y = 19 - (tetris.PosY - tetris.Public_GetBlockYout);
      currBoard[x+y*10] = block;
    }

    
    for (int j = 0; j < 20; j++) {
      for (int i = 0; i < 10; i++) {
        if(lastBoard[i+j*10]!=currBoard[i+j*10]){
          lastBoard[i+j*10] = currBoard[i+j*10];
          int block = lastBoard[i+j*10];
          drawImgWin((void *)(res + TETRIS_RES + block*20*20*2), i*20 + 4, j*20 + 16 + 4, 20, 20);
        }
      }
    }

    for (int i = 0; i < 4; i++) {
      for (int j = 0; j < 4; j++) {
        currNext[i+j*4] = 0;
      }
    }

    for (int idx = 0; idx < 4; idx++) {
      int block = tetris.Public_GetBlock(tetris.NextShapeNo, 0, idx);
      int i = tetris.Public_GetBlockXout;
      int j = tetris.Public_GetBlockYout;
      currNext[i+j*4] = block;
    }

    for (int j = 0; j < 4; j++) {
      for (int i = 0; i < 4; i++) {
        if(lastNext[i+j*4]!=currNext[i+j*4]){
          int block = lastNext[i+j*4] = currNext[i+j*4];
          drawImgWin((void *)(res + TETRIS_RES + block*20*20*2), i*20 + 220, j*20 + 26 + 4, 20, 20);
        }
      }
    }
  }

  static void draw(Form* self){
    FormTetris* form = (FormTetris*)self;
    form->InitBoard();
    form->DrawBoard();

  }


  static void close(Form* window){
    // windows->removeTimer(formTetris->timer);
    // windows->removeWindow((Window*)formTetris);
    // windows->refresh();
    // formTetris=0;

  }

  static void dispose(Window* self){
    // free(((FormTetris*)self)->timer);
    // ((FormTetris*)self)->dispose();
  }

};

void onTetrisTimer(Timer_win* timer) {
  formTetris->onTimer();
}
